﻿using UnityEngine;
using System.Collections;


/// <summary>
/// 세력
/// </summary>
public enum Faction
{
	Allies,												// 아군 (플레이어편)
	Foes,												// 적군 (몬스터)
	Neutral,											// 중립
}

public class BaseCharacter : VulnerableObject, IAttacker
{
	/// 캐릭터 공격 타입
	public enum AttackType
	{
		None,

		Melee,											// 근접 (몸통으로 부딪치면 공격)
		Ranged,											// 원거리
	}

	/// 방향
	public enum Direction
	{
		None,
		Left,
		Right,
	}

													
	// Properties							   	

	[SerializeField]									
	float			m_attack		= 1f;				// 공격력
	[SerializeField]									
	AttackType		m_attackType;						// 공격 종류
	[SerializeField]									
	float			m_attackForce	= 1f;				// 공격시 적용되는 물리력
	[SerializeField]									
	float			m_moveSpeed		= 1f;				// 이동 속도
	[SerializeField]
	float			m_jumpForce		= 1f;				// 점프하는 힘

	[SerializeField]
	string			m_bulletPath;						// 총알 프리팹 경로
	[SerializeField]
	float			m_bulletXOffset	= 0f;				// 총알 발사되는 x 오프셋
	[SerializeField]
	float			m_bulletYOffset	= 0f;				// 총알 발사되는 y 오프셋

	[SerializeField]
	string			m_deathEffectPath;					// 죽을 때 생성되는 이펙트



	// Members
	
	Rigidbody2D		m_rigidBody;
	Direction		m_currentDir;						// 현재 캐릭터 방향

	bool			m_hidden	= false;				// 현재 숨김 여부



	/// <summary>
	/// 현재 캐릭터 방향
	/// </summary>
	/// <value>The current direction.</value>
	public Direction currentDirection
	{
		get
		{
			return m_currentDir;
		}

		set
		{
			if(m_currentDir != value)
			{
				m_currentDir	= value;

				Vector3 scale	= localT.localScale;	// * 방향에 따라 스프라이트 좌우 반전해주기
				scale.x			= Mathf.Abs(scale.x) * -directionFactor;
				localT.localScale	= scale;
			}
		}
	}

	/// <summary>
	/// 바라보는 방향에 따라 1, -1 리턴 (x축 방향에 따라 결정)
	/// </summary>
	/// <value>The direction factor.</value>
	public float directionFactor
	{
		get
		{
			return m_currentDir == Direction.Left? -1 : 1;
		}
	}

	/// <summary>
	/// 데미지 (근접 공격일 때만 해당됨)
	/// </summary>
	/// <value>The damage.</value>
	public float damage
	{
		get
		{
			return m_attack;
		}
	}

	/// <summary>
	/// 공격시 물리력 (근접 공격일 때만 해당됨)
	/// </summary>
	/// <value>The attack force.</value>
	public float attackForce
	{
		get
		{
			return m_attackForce;
		}
	}

	/// <summary>
	/// 유효한 공격자인지 여부
	/// </summary>
	/// <value>true</value>
	/// <c>false</c>
	public bool validAttacker
	{
		get
		{
			return curHP > 0 && m_attackType == AttackType.Melee;		// 근접 전투 캐릭터면서 아직 죽지 않았을 때만 유효함.
		}
	}

	/// <summary>
	/// 게임 월드 상에서의 좌표를 구한다 (= localPosition)
	/// </summary>
	/// <value>The position.</value>
	public Vector2 position
	{
		get
		{
			Vector3 pos	= localT.localPosition;
			return new Vector2(pos.x, pos.y);
		}
	}


	public bool isHidden
	{
		get
		{
			return m_hidden;
		}
		set
		{
			if(m_hidden != value)			// * 상태가 변할 때, rigidbody 휴면 설정도 같이 해줌
			{
				rigidbody2D.isKinematic	= value;
//				if(value)
//					rigidbody2D.Sleep();
//				else
//					rigidbody2D.WakeUp();
			}

			m_hidden			= value;
			renderer.enabled	= !value;	// renderer 활성화를 설정한다
			collider2D.enabled	= !value;
		}
	}



	public override void OnInitialize ()
	{
		base.OnInitialize ();

		m_rigidBody	= rigidbody2D;
	}


	/// <summary>
	/// 현재 향한 방향으로 한 프레임 분량만큼 이동한다. (단, FixedUpdate 에만 실행해줘야한다!)
	/// </summary>
	public void Move()
	{
		if(currentDirection != Direction.None)			// * 반드시 지정된 방향이 있을 때만 움직일 수 있다
		{
			float dirFactor			= directionFactor;
			float distForFixedFrame	= Time.fixedDeltaTime * m_moveSpeed;
			localT.Translate(Vector3.right * dirFactor * distForFixedFrame);
		}
	}

	/// <summary>
	/// 점프
	/// </summary>
	public void Jump()
	{
		RaycastHit2D	ceilHit	= Physics2D.Raycast(localT.position + Vector3.up * 8.1f, Vector2.up, 0.1f);
		bool noCeil		= ceilHit.collider == null || ceilHit.collider.gameObject.layer != LayerMask.NameToLayer("Obstacle");
		if(noCeil && Mathf.Abs(m_rigidBody.velocity.y) < 0.01f)	// * Y축 이동이 없는 상태를 지면에 안착한 상태로 인식한다
		//if(hit.collider != null)
		{
			m_rigidBody.AddForce(Vector3.up * m_jumpForce);

			SoundManager.PlaySfx(SoundInfos.GetSfx("jump"), transform);
		}
	}

	/// <summary>
	/// 원거리 공격 타입일 경우 공격하기
	/// </summary>
	public void Shoot()
	{
		if(m_attackType == AttackType.Ranged)										// * 원거리 타입일 때만 반응
		{
			GameObject prefab	= ResourceDepot.Load<GameObject>(m_bulletPath);		// 로드/생성
			GameObject bltGo	= Object.Instantiate(prefab) as GameObject;
			prefab				= null;

			Transform bulletT	= bltGo.transform;									// 방향과 오프셋 맞춰주기
			bulletT.position	= localT.position 
									+ new Vector3(m_bulletXOffset * directionFactor, m_bulletYOffset); 
			Bullet bullet		= bltGo.GetComponent<Bullet>();
			bullet.Shoot(this, Vector2.right * directionFactor);

			SoundManager.PlaySfx(SoundInfos.GetSfx("shoot"), transform);
		}
	}




	
	/// <summary>
	/// 공격이 맞았을 때
	/// </summary>
	/// <param name="target">Target.</param>
	public virtual void OnHit(IVulnerable target)
	{

	}

	/// <summary>
	/// 죽었을 때 - 기본 행동은 그냥 오브젝트 제거
	/// </summary>
	protected override void OnDie ()
	{
		GameObject efprefab	= ResourceDepot.Load<GameObject>(m_deathEffectPath);
		Object.Instantiate(efprefab, localT.position, Quaternion.identity);

		Object.Destroy(gameObject);
	}
}
